package com.yxf.variousshapes3d.programs;


import android.util.Log;

import com.yxf.variousshapes3d.CommonUtils;
import com.yxf.variousshapes3d.R;
import com.yxf.variousshapes3d.shapes.BaseShape;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniformMatrix4fv;
import static android.opengl.Matrix.multiplyMM;
import static android.opengl.Matrix.orthoM;
import static android.opengl.Matrix.rotateM;
import static android.opengl.Matrix.setIdentityM;
import static android.opengl.Matrix.setLookAtM;
import static android.opengl.Matrix.translateM;

public class ShapeProgram extends Program implements Program.ShaderCallback {

    private static final String A_POSITION = "a_Position";
    private int aPositionLocation;

    private static final String U_MATRIX = "u_Matrix";
    private final float[] projectionMatrix = new float[16];
    private int uMatrixLocation;

    private final float[] modeMatrix = new float[16];

    private static final String A_COLOR = "a_Color";
    private int aColorLocation;

    private List<BaseShape> shapeList = new CopyOnWriteArrayList<BaseShape>();
    private float rotateX, rotateY, rotateZ;
    private int width, height;

    private Object mMatrixLock = new Object();

    public ShapeProgram() {
        super();
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        super.onSurfaceChanged(width, height);
        this.width = width;
        this.height = height;
        initProjectionMatrix();
    }

    private void initProjectionMatrix() {
        synchronized (mMatrixLock) {
            //构建透视投影矩阵
            CommonUtils.perspectiveM(projectionMatrix, 45, (float) width
                    / (float) height, 1f, 10f);
            //重置模型矩阵
            setIdentityM(modeMatrix, 0);
            //设置相机位置
            setLookAtM(modeMatrix, 0, 0f, 3.2f, 3.2f, 0f, 0f, 0f, 0f, 1f, 0f);
            //将模型矩阵和透视投影矩阵相乘获得新的投影矩阵
            final float[] temp = new float[16];
            multiplyMM(temp, 0, projectionMatrix, 0, modeMatrix, 0);
            System.arraycopy(temp, 0, projectionMatrix, 0, temp.length);
        }
    }

    public void addShape(BaseShape baseShape) {
        shapeList.add(baseShape);
        baseShape.initialize();
    }

    public void clearShapeList() {
        shapeList.clear();
    }

    private void initShapeList(float aspectRatio) {
        clearShapeList();
    }

    @Override
    public void onDrawFrame() {
        super.onDrawFrame();
        for (BaseShape baseShape : shapeList) {
            baseShape.setLocation(aPositionLocation, aColorLocation);
            baseShape.draw();
        }
    }

    @Override
    public ShaderCallback getShaderCallback() {
        return this;
    }

    @Override
    public int getVertexResourceId() {
        return R.raw.shape_vertex_shader;
    }

    @Override
    public int getFragmentResourceId() {
        return R.raw.shape_fragment_shader;
    }

    @Override
    public void initialize(int program) {
        aPositionLocation = glGetAttribLocation(program, A_POSITION);
        uMatrixLocation = glGetUniformLocation(program, U_MATRIX);
        aColorLocation = glGetAttribLocation(program, A_COLOR);

        glEnableVertexAttribArray(aPositionLocation);
        glEnableVertexAttribArray(aColorLocation);
    }

    @Override
    public void prepareDraw(int program) {
        synchronized (mMatrixLock) {
            initProjectionMatrix();
            setIdentityM(modeMatrix, 0);
            rotateM(modeMatrix, 0, rotateX, 1f, 0f, 0f);
            rotateM(modeMatrix, 0, rotateY, 0f, 1f, 0f);
            rotateM(modeMatrix, 0, rotateZ, 0f, 0f, 1f);
            float[] temp = new float[16];
            multiplyMM(temp, 0, projectionMatrix, 0, modeMatrix, 0);
            System.arraycopy(temp, 0, projectionMatrix, 0, temp.length);
            Log.d("VariousShapes", "x ; " + rotateX + " , y : " + rotateY);
            glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);
        }
    }

    public void rotate(float x, float y, float z) {
        synchronized (mMatrixLock) {
            rotateX += x;
            rotateY += y;
            rotateZ += z;
        }
    }
}
